// Burn - Rom Loading module
#include "burnint.h"

// Load a rom and separate out the bytes by gap
// dest is the memory block to insert the rom into
static int LoadRom(unsigned char* dest, unsigned i, int gap, int _xor)
{
	if (!burnInterface.getIRomLoader()) {
		return 1; // Load function was not defined by the application
	}

	int ret = 0, len = 0;

	// Find the length of the rom (as given by the current driver)
	{
		struct BurnRomInfo ri;
		ri.nType = 0;
		ri.nLen = 0;
		BurnDrvGetRomInfo(&ri, i);
		if (ri.nType == 0) {
			return 0; // Empty rom slot - don't load anything and return success
		}
		len = ri.nLen;
	}

	if (len <= 0) {
		return 1;
	}

    char* romName;
	BurnDrvGetRomName(&romName, i, 0);

	if (gap > 1 || _xor) {
		// Allocate space for the file
		unsigned char* load = (unsigned char *)malloc(len);
		if (load == NULL) {
			return 1;
		}
		memset(load, 0, len);

		// Load in the file
		unsigned loadLen = 0;
		ret = burnInterface.getIRomLoader()->loadRom(load, &loadLen, i);

		// apply patch, modified by regret
		if (burnInterface.getIPatch()) {
			burnInterface.getIPatch()->apply(load, romName, loadLen);
		}

		if (ret != 0) {
			free(load);
			return 1;
		}

		if (loadLen > len) {
			loadLen = len;
		}

		// Loaded rom okay. Now insert into dest
		unsigned char* LoadEnd = load + loadLen;
		unsigned char* pd = dest;
		unsigned char* pl = load;

		// Quickly copy in the bytes with a gap of 'gap' between each byte
		if (_xor) {
			do {
				*pd ^= *pl++;
				pd += gap;
			} while (pl < LoadEnd);
		} else {
			do {
				*pd = *pl++;
				pd += gap;
			} while (pl < LoadEnd);
		}

		free(load);
	} else {
		// If no XOR, and gap of 1, just copy straight in
		unsigned loadLen = 0;
		ret = burnInterface.getIRomLoader()->loadRom(dest, &loadLen, i);

		// apply patch, modified by regret
		if (burnInterface.getIPatch()) {
			burnInterface.getIPatch()->apply(dest, romName, loadLen);
		}

		if (ret != 0)  {
			return 1;
		}
	}

	return 0;
}

int BurnLoadRom(unsigned char* dest, unsigned i, int gap)
{
	return LoadRom(dest, i, gap, 0);
}

int BurnXorRom(unsigned char* dest, unsigned i, int gap)
{
	return LoadRom(dest, i, gap, 1);
}

// Separate out a bitfield into Bit number 'field' of each nibble in dest
// (end result: each dword in memory carries the 8 pixels of a tile line).
int BurnLoadBitField(unsigned char* dest, unsigned char* src, int field, int srcLen)
{
	int bit;
	for (int nPix = 0; nPix < (srcLen << 3); nPix++) {
		// Get the bitplane pixel value (on or off)
		bit = (*src) >> (7 - (nPix & 7));
		bit &= 1;
		bit <<= field; // Move to correct bit for this field

		// use low nibble for each even pixel
		if ((nPix & 1) == 1) {
			bit <<= 4; // use high nibble for each odd pixel
		}

		*dest |= bit; // OR into destination
		if ((nPix & 1) == 1) {
			dest++;
		}
		if ((nPix & 7) == 7) {
			src++;
		}
	}
	return 0;
}
